#include <utils/traceUtils/registerValue.idc>
#include <utils/traceUtils/forRunToAddr.idc>
#include <utils/traceUtils/segmentUtils.idc>
/**
 * 函数说明：
 * public:
 *      addBlackFunc(name);             添加c方法的黑名单；name是方法名字符串
 *      blackFuncContain(disAsm);       判断当前语句中是否包含c黑名单中的方法，返回1为包含，返回-1为不包含；disAsm是汇编指令
 *      addMsgSendBlackFunc(name);      添加oc方法的黑名单；name是方法名字符串
 *      blackMsgSendContain(disAsm);    判断当前语句中是否包含oc黑名单中的方法，返回1为包含，返回-1为不包含；disAsm是汇编指令
 *      addRunToX30Addr(addr);          添加跳转到X30的地址
 *      isRunToX30(addr);               判断入参地址是否在跳转X30的地址列表里
 *      runToX30();                     动作： 跳转到X30
 *      addForRunToAddr(segmentName, baseAddr, startAddr, endAddr);          添加地址跳转方法，效果当执行到startAddr方法时直接执行到endAddr；segmentName是段名称，baseAddr是基址，startAddr是开始跳转的地址，endAddr是跳转到的地址
 *      addForRunToAddrByUser(startAddr, endAddr);           添加地址跳转方法，效果当执行到startAddr方法时直接执行到endAddr；startAddr是开始跳转的地址，endAddr是跳转到的地址
 *      goBeyondAddrHandle(addr);       超出地址跳转逻辑           
*/
class JumpRule{
    JumpRule(){
        this.blackFuncNames = Array(AR_STR, "blackFuncNames");
        this.objcMsgSendBlackFunc = Array(AR_STR, "objcMsgSendBlack");
        this.runToX30AddrArray = Array(AR_LONG, "runToX30AddrArray");

        this.bblQueue = Queue(AR_STR, "bblQueue", 500);

        this.forRunToAddr = ForRunToAddr();
        this.registerValue = RegisterValue();
        this.segmentUtils = SegmentUtils();
        // this.segmentUtils.forEcho();

        this.determineLibSystemPthreadSegment();
    }

    ~JumpRule(){}

    /**
     * --------------   blackFunc start  ------------------
     * 函数黑名单
    */
    addBlackFunc(name){
        this.blackFuncNames.add(name);
    }

    blackFuncContain(disAsm){
        if(this.blackFuncNames.contain(disAsm) != -1){
            return 1;
        }
        return -1;
    }
    // --------------   blackFunc end  ------------------

    /**
     * --------------   objcMsgSendBlack start  ------------------
     * msgSend函数黑名单
    */
    blackMsgSendContain(disAsm){
        if(strstr(disAsm, "_objc_msgSend") == -1 && strstr(disAsm, "objc_msgSend") == -1){
            return -1;
        }

        auto realResult = this.registerValue.getSendMsgValue("X1", 80);
        if(this.objcMsgSendBlackFunc.containEqual(realResult) != -1){
            Message("filter _objc_msgSend X1=%s \n", realResult);
            return 1;
        }
        return -1;
    }

    addMsgSendBlackFunc(name){
        this.objcMsgSendBlackFunc.add(name);
    }
    // --------------   objcMsgSendBlack end  ------------------

    /**
     * --------------   runToX30 start  ------------------
     * 指定跳转到X30的地址
    */
    isRunToX30(addr){
        if(this.runToX30AddrArray.contain(addr) != -1){
            return 1;       
        }

        return -1;
    }

    runToX30(){
        auto addr;
        addr = this.registerValue.getAddr("X30");
        Message("x30 addr 0x%x\n", addr);
        RunTo(addr);
    }

    addRunToX30Addr(addr){
        this.runToX30AddrArray.add(addr);
        // this.runToX30AddrArray.arrayEcho();
    }
    // --------------   runToX30 end  ------------------

    /**
     * --------------   forRunToAddr start  ------------------
     * for 循环指定跳转地址
    */
    addForRunToAddr(segmentName, baseAddr, startAddr, endAddr){
        auto baseStartAddr, baseEndAddr;
        baseStartAddr = startAddr - baseAddr;
        baseEndAddr = endAddr - baseAddr;

        auto minAddr, maxAddr;
        minAddr = this.segmentUtils.getStartAddrBySegmentName(segmentName);
        if(minAddr == 0xffffffffffffffff){
            Warning("add for run to addr get segment min fail ！！！ \n");
            return 0;
        }

        auto tmpMinAddr, tmpMaxAddr;
        tmpMinAddr = minAddr + baseStartAddr;
        tmpMaxAddr = minAddr + baseEndAddr;
        this.forRunToAddr.add(segmentName, tmpMinAddr, tmpMaxAddr);
        // this.forRunToAddr.forEcho();
    }

    addForRunToAddrByUser(startAddr, endAddr){
        this.forRunToAddr.add("user", startAddr, endAddr);
        // this.forRunToAddr.forEcho();
    }
    // --------------   forRunToAddr end  ------------------

    /**
     * --------------   transcend start  ------------------
     * 超出地址跳转逻辑
    */
    goBeyondAddrHandle(addr, text_min_addr, text_max_addr){
        if(addr == 0xffffffffffffffff){
            Sleep(50);
            StepInto();
            return;
        }

        // 美团临时添加判断逻辑，之后可以抽出来
        auto segName = SegName(addr);
        // Message("segName = " + segName);
        if(segName == "libsystem_malloc.dylib:__text"){
            this.runToX30();
            return;
        }

        if(this.isBBlJumpFunc() != -1){
            this.runToX30();
            return;
        }

        this.forRunToAddr.addQueueAddr(addr);
        if(this.forRunToAddr.checkFor() == 1){
            Sleep(50);
            this.forRunToAddr.addForJumpAddrBpt();
            StepOver();
            return;
        }

        auto segmentName, forEndAddr;
        auto forRunToAddrIndex = this.forRunToAddr.getForStartAddrIndex(addr);
        if(forRunToAddrIndex != -1){
            segmentName = this.forRunToAddr.getSegmentNameByIndex(forRunToAddrIndex);
            forEndAddr = this.forRunToAddr.getForEndAddrByIndex(forRunToAddrIndex);
            Message("%s for handle now addr 0x%x run to 0x%x \n", segmentName, addr, forEndAddr);
            Sleep(50);
            RunTo(forEndAddr);
            return;
        }

        Sleep(50);
        StepInto();
    }

    determineLibSystemPthreadSegment(){
        auto minAddr, maxAddr;
        minAddr = this.segmentUtils.getStartAddrBySegmentName("libsystem_pthread.dylib");
        if(minAddr == 0xffffffffffffffff){
            return 0;
        }

        maxAddr = this.segmentUtils.getEndAddrBySegmentName("libsystem_pthread.dylib");
        if(maxAddr == 0xffffffffffffffff){
            return 0;
        }

        this.libSystemPthreadStart = minAddr;
        this.libSystemPthreadEnd = maxAddr;
        return 1;
    }
    // --------------   transcend end  ------------------

    checkStepOver(addr){
        auto mnem, inst, disAsm;
        mnem = GetMnem(addr);
        if(mnem == "BL" || mnem == "B"){
            inst = FindCode(PrevAddr(addr), SEARCH_DOWN | SEARCH_NEXT);
            disAsm = GetDisasm(inst);
            if(this.blackFuncContain(disAsm) != -1 || this.blackMsgSendContain(disAsm) != -1){
                Message("StepOver by %s\n", disAsm);
                return 1;
            }
        }
        return -1;
    }

    // B BL func jump x30 start ----------------------
    addBBlJumpDisAsmName(funcName){
        this.bblQueue.push(funcName);
    }

    addrAddBBlJumpFunc(addr){
        auto mnem, inst, disAsm;
        mnem = GetMnem(addr);
        if(mnem == "BL" || mnem == "B"){
            inst = FindCode(PrevAddr(addr), SEARCH_DOWN | SEARCH_NEXT);
            disAsm = GetDisasm(inst);
            this.addBBlJumpDisAsmName(disAsm);
        }
    }

    isBBlJumpFunc(){
        auto disAsm = this.bblQueue.getLast();
        if(strstr(disAsm, "releaseReturnValue") != -1){
            return 1;
        }

        if(strstr(disAsm, "releaseReturnValue") != -1){
            return 1;
        }

        if(strstr(disAsm, "setProperty") != -1){
            return 1;
        }
        
        if(strstr(disAsm, "getProperty") != -1){
            return 1;
        }
        return -1;
    }
    // B BL func jump x30 end ------------------------


}